/*
 * Copyright (c) 2015, the DeltaSync# project. All Rights Reserved.
 * This project is a C# port of the JDeltaSync project:
 *     http://jdeltasync.googlecode.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System;
using System.IO;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace PM.Net.DeltaSync {
    /**
     * {@link Store} implementation which saves data in the file system.
     *
     */
    public class DiskStore : AbstractStore {
        //private static Logger log = LoggerFactory.getLogger(typeof(DiskStore));
        
        //private static int MAX_ENTRIES = 32;
        
        /*private Dictionary<string, State> states = new Dictionary<string, State>(16, 0.75f, true) {
            protected boolean removeEldestEntry(Map.Entry<string, State> eldest) {
                return size() > MAX_ENTRIES;
            }
        };*/
        private Dictionary<string, State> states = new Dictionary<string, State>();
        private DirectoryInfo             datadir;
    
        public DiskStore(string datadir) {
            this.datadir = new DirectoryInfo(datadir);
            if (!this.datadir.Exists)
                throw new DirectoryNotFoundException("Directory not available: " + datadir);
        }
    
        private FileInfo getFile(string username) {
            return new FileInfo(Path.Combine(datadir.FullName, username + ".bin"));
        }
        
        protected override State GetState(string username) {
            State state = states.ContainsKey(username) ? states[username] : null;
            if (state == null) {
                try {
                    FileInfo file = getFile(username);
                    if (file.Exists) {
                        //log.debug("Reading State for user {} from disk", username);
                        IFormatter formatter = new BinaryFormatter();
                        using (var stream = file.OpenRead())
                            state = (State)formatter.Deserialize(stream);
                    }
                } catch (Exception ex) {
                    System.Diagnostics.Debug.WriteLine("Failed to read State object from file for user " + username + "\nReason: " + ex.Message);
                }
                
                if (state == null)
                    state = new State();
                states.Add(username, state);
            }
            return state;
        }
        
        protected override void StateChanged(string username, State state) {
            try {
                FileInfo file = getFile(username);
                FileInfo tmp  = new FileInfo(Path.Combine(file.Directory.FullName, Path.GetFileNameWithoutExtension(file.Name) + ".tmp"));
                FileInfo bkp  = new FileInfo(Path.Combine(file.Directory.FullName, Path.GetFileNameWithoutExtension(file.Name) + ".bkp"));
                //log.debug("Writing State for user {} to disk", username);
                IFormatter formatter = new BinaryFormatter();
                using (Stream stream = tmp.Create())
                    formatter.Serialize(stream, state);
                if (file.Exists)
                    tmp.Replace(file.FullName, bkp.FullName);
                else
                    tmp.MoveTo (file.FullName);
            } catch (Exception ex) {
                System.Diagnostics.Debug.WriteLine("Failed to write State object to file for user " + username + "\nReason: " + ex.Message);
            }
        }
    }
}